package lsp.connector;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;

import lsp.base.LspPacket;
import lsp.base.LspParams;
import lsp.base.MessageType;
import lsp.base.Pack;
import lsp.receiver.LspReceiver;
import lsp.sender.LspSender;
import lsp.timer.LspTimer;

public abstract class AbstractConnector implements IConnector {

	protected DatagramSocket senderSocket;
	private InetAddress address;
	private int port;
	
	private short connectionId;
	private LspPacket currentLspPacket;

	private LinkedBlockingQueue<Pack> receivedPackQueue = new LinkedBlockingQueue<>();
	private LinkedBlockingQueue<LspPacket> receivedLspPacketQueue = new LinkedBlockingQueue<>();
	
	protected LspSender lspSender;
	protected LspTimer lspTimer;
	protected LspReceiver lspReceiver;
	private List<Thread> threadServices = new ArrayList<>();
	
	public AbstractConnector(InetAddress address, int port, LspParams params) throws SocketException {
		this.address = address;
		this.port = port;
		this.senderSocket = new DatagramSocket();
		this.senderSocket.setReuseAddress(true);
		
		lspSender = new LspSender(this);
		lspTimer = new LspTimer(this, params);
		lspReceiver = new LspReceiver(this);
	}
	
	protected void startServices() {
		Thread threadSender = new Thread(lspSender);
		threadSender.start();
		threadServices.add(threadSender);
		
		Thread threadReceiver = new Thread(lspReceiver);
		threadReceiver.start();
		threadServices.add(threadReceiver);
		
		Thread threadTimer = new Thread(lspTimer);
		threadTimer.start();
		threadServices.add(threadTimer);
	}
	
	private void stopServices() {
		for (Thread service: threadServices){ 
			service.interrupt();
		}
	}
	
	@Override
	public boolean isConnected() {
		return connectionId != 0;
	}

	@Override
	public void setConnectionId(short connectionId) {
		this.connectionId = connectionId;
	}
	
	@Override
	public short getConnectionId() {
		return this.connectionId;
	}
	
	protected int getLocalPort() {
		return senderSocket.getLocalPort();
	}
	
	@Override
	public LspPacket getCurrentLspPacket() {
		return currentLspPacket;
	}

	public void setCurrentLspPacket(LspPacket currentLspPacket) {
		this.currentLspPacket = currentLspPacket;
	}
	
	@Override
	public void addReceivedExternalPack(Pack lspPacket) {
		receivedPackQueue.add(lspPacket);
	}

	@Override
	public Pack popReceivedExternalPack() {
		return receivedPackQueue.poll();
	}
	
	@Override
	public void addReceivedLspPacket(LspPacket lspPacket) {
		receivedLspPacketQueue.add(lspPacket);
	}
	
	@Override
	public LspPacket popReceivedLspPacket() {
		return receivedLspPacketQueue.poll();
	}
	
	@Override
	public void sendPacket(LspPacket lspPacket) {
		if (lspPacket.getMessageType() == MessageType.DATA) {
			setCurrentLspPacket(lspPacket);
		}
		
		DatagramPacket datagramPacket = lspPacket.toDatagramPacket();
		datagramPacket.setAddress(address);
		datagramPacket.setPort(port);
		try {
			senderSocket.send(datagramPacket);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	@Override
	public void close() {
		stopServices();
		senderSocket.close();
		connectionId = 0;
	}

}
